From: Roan Kattouw Date: Thu, 15 Jan 2009 10:18:44 +0000 (+0000) Subject: * svn:eol-style native for r45755 X-Git-Tag: 1.31.0-rc.0~43408 X-Git-Url: http://git.cyclocoop.org/%7D%7Cconcat%7B?a=commitdiff_plain;h=ecf739b57a0e60e5e34d98e2d04e284054629741;p=lhc%2Fweb%2Fwiklou.git * svn:eol-style native for r45755 * Replace spaces with tabs in ApiFormatJson_json.php, fix some crazy indentation --- diff --git a/includes/SearchIBM_DB2.php b/includes/SearchIBM_DB2.php index eba401ce7c..57813a73de 100644 --- a/includes/SearchIBM_DB2.php +++ b/includes/SearchIBM_DB2.php @@ -1,247 +1,247 @@ - -# http://www.mediawiki.org/ -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# http://www.gnu.org/copyleft/gpl.html - -/** - * @file - * @ingroup Search - */ - -/** - * Search engine hook base class for IBM DB2 - * @ingroup Search - */ -class SearchIBM_DB2 extends SearchEngine { - function __construct($db) { - $this->db = $db; - } - - /** - * Perform a full text search query and return a result set. - * - * @param string $term - Raw search term - * @return IBM_DB2SearchResultSet - * @access public - */ - function searchText( $term ) { - $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true))); - return new IBM_DB2SearchResultSet($resultSet, $this->searchTerms); - } - - /** - * Perform a title-only search query and return a result set. - * - * @param string $term - Raw search term - * @return IBM_DB2SearchResultSet - * @access public - */ - function searchTitle($term) { - $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false))); - return new MySQLSearchResultSet($resultSet, $this->searchTerms); - } - - - /** - * Return a partial WHERE clause to exclude redirects, if so set - * @return string - * @private - */ - function queryRedirect() { - if ($this->showRedirects) { - return ''; - } else { - return 'AND page_is_redirect=0'; - } - } - - /** - * Return a partial WHERE clause to limit the search to the given namespaces - * @return string - * @private - */ - function queryNamespaces() { - if( is_null($this->namespaces) ) - return ''; - $namespaces = implode(',', $this->namespaces); - if ($namespaces == '') { - $namespaces = '0'; - } - return 'AND page_namespace IN (' . $namespaces . ')'; - } - - /** - * Return a LIMIT clause to limit results on the query. - * @return string - * @private - */ - function queryLimit($sql) { - return $this->db->limitResult($sql, $this->limit, $this->offset); - } - - /** - * Does not do anything for generic search engine - * subclasses may define this though - * @return string - * @private - */ - function queryRanking($filteredTerm, $fulltext) { - // requires Net Search Extender or equivalent - // return ' ORDER BY score(1)'; - return ''; - } - - /** - * Construct the full SQL query to do the search. - * The guts shoulds be constructed in queryMain() - * @param string $filteredTerm - * @param bool $fulltext - * @private - */ - function getQuery( $filteredTerm, $fulltext ) { - return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' . - $this->queryRedirect() . ' ' . - $this->queryNamespaces() . ' ' . - $this->queryRanking( $filteredTerm, $fulltext ) . ' '); - } - - - /** - * Picks which field to index on, depending on what type of query. - * @param bool $fulltext - * @return string - */ - function getIndexField($fulltext) { - return $fulltext ? 'si_text' : 'si_title'; - } - - /** - * Get the base part of the search query. - * - * @param string $filteredTerm - * @param bool $fulltext - * @return string - * @private - */ - function queryMain( $filteredTerm, $fulltext ) { - $match = $this->parseQuery($filteredTerm, $fulltext); - $page = $this->db->tableName('page'); - $searchindex = $this->db->tableName('searchindex'); - return 'SELECT page_id, page_namespace, page_title ' . - "FROM $page,$searchindex " . - 'WHERE page_id=si_page AND ' . $match; - } - - /** @todo document */ - function parseQuery($filteredText, $fulltext) { - global $wgContLang; - $lc = SearchEngine::legalSearchChars(); - $this->searchTerms = array(); - - # FIXME: This doesn't handle parenthetical expressions. - $m = array(); - $q = array(); - - if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', - $filteredText, $m, PREG_SET_ORDER)) { - foreach($m as $terms) { - $q[] = $terms[1] . $wgContLang->stripForSearch($terms[2]); - - if (!empty($terms[3])) { - $regexp = preg_quote( $terms[3], '/' ); - if ($terms[4]) - $regexp .= "[0-9A-Za-z_]+"; - } else { - $regexp = preg_quote(str_replace('"', '', $terms[2]), '/'); - } - $this->searchTerms[] = $regexp; - } - } - - $searchon = $this->db->strencode(join(',', $q)); - $field = $this->getIndexField($fulltext); - - // requires Net Search Extender or equivalent - //return " CONTAINS($field, '$searchon') > 0 "; - - return " lcase($field) LIKE lcase('%$searchon%')"; - } - - /** - * Create or update the search index record for the given page. - * Title and text should be pre-processed. - * - * @param int $id - * @param string $title - * @param string $text - */ - function update($id, $title, $text) { - $dbw = wfGetDB(DB_MASTER); - $dbw->replace('searchindex', - array('si_page'), - array( - 'si_page' => $id, - 'si_title' => $title, - 'si_text' => $text - ), 'SearchIBM_DB2::update' ); - // ? - //$dbw->query("CALL ctx_ddl.sync_index('si_text_idx')"); - //$dbw->query("CALL ctx_ddl.sync_index('si_title_idx')"); - } - - /** - * Update a search index record's title only. - * Title should be pre-processed. - * - * @param int $id - * @param string $title - */ - function updateTitle($id, $title) { - $dbw = wfGetDB(DB_MASTER); - - $dbw->update('searchindex', - array('si_title' => $title), - array('si_page' => $id), - 'SearchIBM_DB2::updateTitle', - array()); - } -} - -/** - * @ingroup Search - */ -class IBM_DB2SearchResultSet extends SearchResultSet { - function __construct($resultSet, $terms) { - $this->mResultSet = $resultSet; - $this->mTerms = $terms; - } - - function termMatches() { - return $this->mTerms; - } - - function numRows() { - return $this->mResultSet->numRows(); - } - - function next() { - $row = $this->mResultSet->fetchObject(); - if ($row === false) - return false; - return new SearchResult($row); - } -} + +# http://www.mediawiki.org/ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# http://www.gnu.org/copyleft/gpl.html + +/** + * @file + * @ingroup Search + */ + +/** + * Search engine hook base class for IBM DB2 + * @ingroup Search + */ +class SearchIBM_DB2 extends SearchEngine { + function __construct($db) { + $this->db = $db; + } + + /** + * Perform a full text search query and return a result set. + * + * @param string $term - Raw search term + * @return IBM_DB2SearchResultSet + * @access public + */ + function searchText( $term ) { + $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true))); + return new IBM_DB2SearchResultSet($resultSet, $this->searchTerms); + } + + /** + * Perform a title-only search query and return a result set. + * + * @param string $term - Raw search term + * @return IBM_DB2SearchResultSet + * @access public + */ + function searchTitle($term) { + $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false))); + return new MySQLSearchResultSet($resultSet, $this->searchTerms); + } + + + /** + * Return a partial WHERE clause to exclude redirects, if so set + * @return string + * @private + */ + function queryRedirect() { + if ($this->showRedirects) { + return ''; + } else { + return 'AND page_is_redirect=0'; + } + } + + /** + * Return a partial WHERE clause to limit the search to the given namespaces + * @return string + * @private + */ + function queryNamespaces() { + if( is_null($this->namespaces) ) + return ''; + $namespaces = implode(',', $this->namespaces); + if ($namespaces == '') { + $namespaces = '0'; + } + return 'AND page_namespace IN (' . $namespaces . ')'; + } + + /** + * Return a LIMIT clause to limit results on the query. + * @return string + * @private + */ + function queryLimit($sql) { + return $this->db->limitResult($sql, $this->limit, $this->offset); + } + + /** + * Does not do anything for generic search engine + * subclasses may define this though + * @return string + * @private + */ + function queryRanking($filteredTerm, $fulltext) { + // requires Net Search Extender or equivalent + // return ' ORDER BY score(1)'; + return ''; + } + + /** + * Construct the full SQL query to do the search. + * The guts shoulds be constructed in queryMain() + * @param string $filteredTerm + * @param bool $fulltext + * @private + */ + function getQuery( $filteredTerm, $fulltext ) { + return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' . + $this->queryRedirect() . ' ' . + $this->queryNamespaces() . ' ' . + $this->queryRanking( $filteredTerm, $fulltext ) . ' '); + } + + + /** + * Picks which field to index on, depending on what type of query. + * @param bool $fulltext + * @return string + */ + function getIndexField($fulltext) { + return $fulltext ? 'si_text' : 'si_title'; + } + + /** + * Get the base part of the search query. + * + * @param string $filteredTerm + * @param bool $fulltext + * @return string + * @private + */ + function queryMain( $filteredTerm, $fulltext ) { + $match = $this->parseQuery($filteredTerm, $fulltext); + $page = $this->db->tableName('page'); + $searchindex = $this->db->tableName('searchindex'); + return 'SELECT page_id, page_namespace, page_title ' . + "FROM $page,$searchindex " . + 'WHERE page_id=si_page AND ' . $match; + } + + /** @todo document */ + function parseQuery($filteredText, $fulltext) { + global $wgContLang; + $lc = SearchEngine::legalSearchChars(); + $this->searchTerms = array(); + + # FIXME: This doesn't handle parenthetical expressions. + $m = array(); + $q = array(); + + if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', + $filteredText, $m, PREG_SET_ORDER)) { + foreach($m as $terms) { + $q[] = $terms[1] . $wgContLang->stripForSearch($terms[2]); + + if (!empty($terms[3])) { + $regexp = preg_quote( $terms[3], '/' ); + if ($terms[4]) + $regexp .= "[0-9A-Za-z_]+"; + } else { + $regexp = preg_quote(str_replace('"', '', $terms[2]), '/'); + } + $this->searchTerms[] = $regexp; + } + } + + $searchon = $this->db->strencode(join(',', $q)); + $field = $this->getIndexField($fulltext); + + // requires Net Search Extender or equivalent + //return " CONTAINS($field, '$searchon') > 0 "; + + return " lcase($field) LIKE lcase('%$searchon%')"; + } + + /** + * Create or update the search index record for the given page. + * Title and text should be pre-processed. + * + * @param int $id + * @param string $title + * @param string $text + */ + function update($id, $title, $text) { + $dbw = wfGetDB(DB_MASTER); + $dbw->replace('searchindex', + array('si_page'), + array( + 'si_page' => $id, + 'si_title' => $title, + 'si_text' => $text + ), 'SearchIBM_DB2::update' ); + // ? + //$dbw->query("CALL ctx_ddl.sync_index('si_text_idx')"); + //$dbw->query("CALL ctx_ddl.sync_index('si_title_idx')"); + } + + /** + * Update a search index record's title only. + * Title should be pre-processed. + * + * @param int $id + * @param string $title + */ + function updateTitle($id, $title) { + $dbw = wfGetDB(DB_MASTER); + + $dbw->update('searchindex', + array('si_title' => $title), + array('si_page' => $id), + 'SearchIBM_DB2::updateTitle', + array()); + } +} + +/** + * @ingroup Search + */ +class IBM_DB2SearchResultSet extends SearchResultSet { + function __construct($resultSet, $terms) { + $this->mResultSet = $resultSet; + $this->mTerms = $terms; + } + + function termMatches() { + return $this->mTerms; + } + + function numRows() { + return $this->mResultSet->numRows(); + } + + function next() { + $row = $this->mResultSet->fetchObject(); + if ($row === false) + return false; + return new SearchResult($row); + } +} diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php index e28cbb37e0..f86ec03864 100644 --- a/includes/api/ApiFormatJson_json.php +++ b/includes/api/ApiFormatJson_json.php @@ -45,14 +45,14 @@ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * -* @ingroup API -* @author Michal Migurski -* @author Matt Knapp -* @author Brett Stimmerman -* @copyright 2005 Michal Migurski -* @version CVS: $Id$ -* @license http://www.opensource.org/licenses/bsd-license.php -* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198 +* @ingroup API +* @author Michal Migurski +* @author Matt Knapp +* @author Brett Stimmerman +* @copyright 2005 Michal Migurski +* @version CVS: $Id$ +* @license http://www.opensource.org/licenses/bsd-license.php +* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198 */ /** @@ -115,715 +115,715 @@ define('SERVICES_JSON_SUPPRESS_ERRORS', 32); */ class Services_JSON { - /** - * constructs a new JSON instance - * - * @param int $use object behavior flags; combine with boolean-OR - * - * possible values: - * - SERVICES_JSON_LOOSE_TYPE: loose typing. - * "{...}" syntax creates associative arrays - * instead of objects in decode(). - * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. - * Values which can't be encoded (e.g. resources) - * appear as NULL instead of throwing errors. - * By default, a deeply-nested resource will - * bubble up with an error, so all return values - * from encode() should be checked with isError() - */ - function Services_JSON($use = 0) - { - $this->use = $use; - } - - /** - * convert a string from one UTF-16 char to one UTF-8 char - * - * Normally should be handled by mb_convert_encoding, but - * provides a slower PHP-only method for installations - * that lack the multibye string extension. - * - * @param string $utf16 UTF-16 character - * @return string UTF-8 character - * @access private - */ - function utf162utf8($utf16) - { - // oh please oh please oh please oh please oh please - if(function_exists('mb_convert_encoding')) { - return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); - } - - $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); - - switch(true) { - case ((0x7F & $bytes) == $bytes): - // this case should never be reached, because we are in ASCII range - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0x7F & $bytes); - - case (0x07FF & $bytes) == $bytes: - // return a 2-byte UTF-8 character - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0xC0 | (($bytes >> 6) & 0x1F)) - . chr(0x80 | ($bytes & 0x3F)); - - case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16{2})) == 0xDC: - // return a 4-byte UTF-8 character - $char = ((($bytes & 0x03FF) << 10) - | ((ord($utf16{2}) & 0x03) << 8) - | ord($utf16{3})); - $char += 0x10000; - return chr(0xF0 | (($char >> 18) & 0x07)) - . chr(0x80 | (($char >> 12) & 0x3F)) - . chr(0x80 | (($char >> 6) & 0x3F)) - . chr(0x80 | ($char & 0x3F)); - - case (0xFFFF & $bytes) == $bytes: - // return a 3-byte UTF-8 character - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0xE0 | (($bytes >> 12) & 0x0F)) - . chr(0x80 | (($bytes >> 6) & 0x3F)) - . chr(0x80 | ($bytes & 0x3F)); - } - - // ignoring UTF-32 for now, sorry - return ''; - } - - /** - * convert a string from one UTF-8 char to one UTF-16 char - * - * Normally should be handled by mb_convert_encoding, but - * provides a slower PHP-only method for installations - * that lack the multibye string extension. - * - * @param string $utf8 UTF-8 character - * @return string UTF-16 character - * @access private - */ - function utf82utf16($utf8) - { - // oh please oh please oh please oh please oh please - if(function_exists('mb_convert_encoding')) { - return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); - } - - switch(strlen($utf8)) { - case 1: - // this case should never be reached, because we are in ASCII range - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return $utf8; - - case 2: - // return a UTF-16 character from a 2-byte UTF-8 char - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0x07 & (ord($utf8{0}) >> 2)) - . chr((0xC0 & (ord($utf8{0}) << 6)) - | (0x3F & ord($utf8{1}))); - - case 3: - // return a UTF-16 character from a 3-byte UTF-8 char - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr((0xF0 & (ord($utf8{0}) << 4)) - | (0x0F & (ord($utf8{1}) >> 2))) - . chr((0xC0 & (ord($utf8{1}) << 6)) - | (0x7F & ord($utf8{2}))); - - case 4: - // return a UTF-16 surrogate pair from a 4-byte UTF-8 char - if(ord($utf8{0}) > 0xF4) return ''; # invalid - $char = ((0x1C0000 & (ord($utf8{0}) << 18)) - | (0x03F000 & (ord($utf8{1}) << 12)) - | (0x000FC0 & (ord($utf8{2}) << 6)) - | (0x00003F & ord($utf8{3}))); - if($char > 0x10FFFF) return ''; # invalid - $char -= 0x10000; - return chr(0xD8 | (($char >> 18) & 0x03)) - . chr(($char >> 10) & 0xFF) - . chr(0xDC | (($char >> 8) & 0x03)) - . chr($char & 0xFF); - } - - // ignoring UTF-32 for now, sorry - return ''; - } - - /** - * encodes an arbitrary variable into JSON format - * - * @param mixed $var any number, boolean, string, array, or object to be encoded. - * see argument 1 to Services_JSON() above for array-parsing behavior. - * if var is a strng, note that encode() always expects it - * to be in ASCII or UTF-8 format! - * @param bool $pretty pretty-print output with indents and newlines - * - * @return mixed JSON string representation of input var or an error if a problem occurs - * @access public - */ - function encode($var, $pretty=false) - { - $this->indent = 0; - $this->pretty = $pretty; - $this->nameValSeparator = $pretty ? ': ' : ':'; - return $this->encode2($var); - } - - /** - * encodes an arbitrary variable into JSON format - * - * @param mixed $var any number, boolean, string, array, or object to be encoded. - * see argument 1 to Services_JSON() above for array-parsing behavior. - * if var is a strng, note that encode() always expects it - * to be in ASCII or UTF-8 format! - * - * @return mixed JSON string representation of input var or an error if a problem occurs - * @access private - */ - function encode2($var) - { - if ($this->pretty) { - $close = "\n" . str_repeat("\t", $this->indent); - $open = $close . "\t"; - $mid = ',' . $open; - } - else { - $open = $close = ''; - $mid = ','; - } - - switch (gettype($var)) { - case 'boolean': - return $var ? 'true' : 'false'; - - case 'NULL': - return 'null'; - - case 'integer': - return (int) $var; - - case 'double': - case 'float': - return (float) $var; - - case 'string': - // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT - $ascii = ''; - $strlen_var = strlen($var); - - /* - * Iterate over every character in the string, - * escaping with a slash or encoding to UTF-8 where necessary - */ - for ($c = 0; $c < $strlen_var; ++$c) { - - $ord_var_c = ord($var{$c}); - - switch (true) { - case $ord_var_c == 0x08: - $ascii .= '\b'; - break; - case $ord_var_c == 0x09: - $ascii .= '\t'; - break; - case $ord_var_c == 0x0A: - $ascii .= '\n'; - break; - case $ord_var_c == 0x0C: - $ascii .= '\f'; - break; - case $ord_var_c == 0x0D: - $ascii .= '\r'; - break; - - case $ord_var_c == 0x22: - case $ord_var_c == 0x2F: - case $ord_var_c == 0x5C: - // double quote, slash, slosh - $ascii .= '\\'.$var{$c}; - break; - - case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): - // characters U-00000000 - U-0000007F (same as ASCII) - $ascii .= $var{$c}; - break; - - case (($ord_var_c & 0xE0) == 0xC0): - // characters U-00000080 - U-000007FF, mask 110XXXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, ord($var{$c + 1})); - $c += 1; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xF0) == 0xE0): - // characters U-00000800 - U-0000FFFF, mask 1110XXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, - ord($var{$c + 1}), - ord($var{$c + 2})); - $c += 2; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xF8) == 0xF0): - // characters U-00010000 - U-001FFFFF, mask 11110XXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - // These will always return a surrogate pair - $char = pack('C*', $ord_var_c, - ord($var{$c + 1}), - ord($var{$c + 2}), - ord($var{$c + 3})); - $c += 3; - $utf16 = $this->utf82utf16($char); - if($utf16 == '') { - $ascii .= '\ufffd'; - } else { - $utf16 = str_split($utf16, 2); - $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1])); - } - break; - } - } - - return '"'.$ascii.'"'; - - case 'array': - /* - * As per JSON spec if any array key is not an integer - * we must treat the the whole array as an object. We - * also try to catch a sparsely populated associative - * array with numeric keys here because some JS engines - * will create an array with empty indexes up to - * max_index which can cause memory issues and because - * the keys, which may be relevant, will be remapped - * otherwise. - * - * As per the ECMA and JSON specification an object may - * have any string as a property. Unfortunately due to - * a hole in the ECMA specification if the key is a - * ECMA reserved word or starts with a digit the - * parameter is only accessible using ECMAScript's - * bracket notation. - */ - - // treat as a JSON object - if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { - $this->indent++; - $properties = array_map(array($this, 'name_value'), - array_keys($var), - array_values($var)); - $this->indent--; - - foreach($properties as $property) { - if(Services_JSON::isError($property)) { - return $property; - } - } - - return '{' . $open . join($mid, $properties) . $close . '}'; - } - - // treat it like a regular array - $this->indent++; - $elements = array_map(array($this, 'encode2'), $var); - $this->indent--; - - foreach($elements as $element) { - if(Services_JSON::isError($element)) { - return $element; - } - } - - return '[' . $open . join($mid, $elements) . $close . ']'; - - case 'object': - $vars = get_object_vars($var); - - $this->indent++; - $properties = array_map(array($this, 'name_value'), - array_keys($vars), - array_values($vars)); - $this->indent--; - - foreach($properties as $property) { - if(Services_JSON::isError($property)) { - return $property; - } - } - - return '{' . $open . join($mid, $properties) . $close . '}'; - - default: - return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) - ? 'null' - : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); - } - } - - /** - * array-walking function for use in generating JSON-formatted name-value pairs - * - * @param string $name name of key to use - * @param mixed $value reference to an array element to be encoded - * - * @return string JSON-formatted name-value pair, like '"name":value' - * @access private - */ - function name_value($name, $value) - { - $encoded_value = $this->encode2($value); - - if(Services_JSON::isError($encoded_value)) { - return $encoded_value; - } - - return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value; - } - - /** - * reduce a string by removing leading and trailing comments and whitespace - * - * @param $str string string value to strip of comments and whitespace - * - * @return string string value stripped of comments and whitespace - * @access private - */ - function reduce_string($str) - { - $str = preg_replace(array( - - // eliminate single line comments in '// ...' form - '#^\s*//(.+)$#m', - - // eliminate multi-line comments in '/* ... */' form, at start of string - '#^\s*/\*(.+)\*/#Us', - - // eliminate multi-line comments in '/* ... */' form, at end of string - '#/\*(.+)\*/\s*$#Us' - - ), '', $str); - - // eliminate extraneous space - return trim($str); - } - - /** - * decodes a JSON string into appropriate variable - * - * @param string $str JSON-formatted string - * - * @return mixed number, boolean, string, array, or object - * corresponding to given JSON input string. - * See argument 1 to Services_JSON() above for object-output behavior. - * Note that decode() always returns strings - * in ASCII or UTF-8 format! - * @access public - */ - function decode($str) - { - $str = $this->reduce_string($str); - - switch (strtolower($str)) { - case 'true': - return true; - - case 'false': - return false; - - case 'null': - return null; - - default: - $m = array(); - - if (is_numeric($str)) { - // Lookie-loo, it's a number - - // This would work on its own, but I'm trying to be - // good about returning integers where appropriate: - // return (float)$str; - - // Return float or int, as appropriate - return ((float)$str == (integer)$str) - ? (integer)$str - : (float)$str; - - } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { - // STRINGS RETURNED IN UTF-8 FORMAT - $delim = substr($str, 0, 1); - $chrs = substr($str, 1, -1); - $utf8 = ''; - $strlen_chrs = strlen($chrs); - - for ($c = 0; $c < $strlen_chrs; ++$c) { - - $substr_chrs_c_2 = substr($chrs, $c, 2); - $ord_chrs_c = ord($chrs{$c}); - - switch (true) { - case $substr_chrs_c_2 == '\b': - $utf8 .= chr(0x08); - ++$c; - break; - case $substr_chrs_c_2 == '\t': - $utf8 .= chr(0x09); - ++$c; - break; - case $substr_chrs_c_2 == '\n': - $utf8 .= chr(0x0A); - ++$c; - break; - case $substr_chrs_c_2 == '\f': - $utf8 .= chr(0x0C); - ++$c; - break; - case $substr_chrs_c_2 == '\r': - $utf8 .= chr(0x0D); - ++$c; - break; - - case $substr_chrs_c_2 == '\\"': - case $substr_chrs_c_2 == '\\\'': - case $substr_chrs_c_2 == '\\\\': - case $substr_chrs_c_2 == '\\/': - if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || - ($delim == "'" && $substr_chrs_c_2 != '\\"')) { - $utf8 .= $chrs{++$c}; - } - break; - - case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)): - // escaped unicode surrogate pair - $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) - . chr(hexdec(substr($chrs, ($c + 4), 2))) - . chr(hexdec(substr($chrs, ($c + 8), 2))) - . chr(hexdec(substr($chrs, ($c + 10), 2))); - $utf8 .= $this->utf162utf8($utf16); - $c += 11; - break; - - case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): - // single, escaped unicode character - $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) - . chr(hexdec(substr($chrs, ($c + 4), 2))); - $utf8 .= $this->utf162utf8($utf16); - $c += 5; - break; - - case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): - $utf8 .= $chrs{$c}; - break; - - case ($ord_chrs_c & 0xE0) == 0xC0: - // characters U-00000080 - U-000007FF, mask 110XXXXX - //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 2); - ++$c; - break; - - case ($ord_chrs_c & 0xF0) == 0xE0: - // characters U-00000800 - U-0000FFFF, mask 1110XXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 3); - $c += 2; - break; - - case ($ord_chrs_c & 0xF8) == 0xF0: - // characters U-00010000 - U-001FFFFF, mask 11110XXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 4); - $c += 3; - break; - - case ($ord_chrs_c & 0xFC) == 0xF8: - // characters U-00200000 - U-03FFFFFF, mask 111110XX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 5); - $c += 4; - break; - - case ($ord_chrs_c & 0xFE) == 0xFC: - // characters U-04000000 - U-7FFFFFFF, mask 1111110X - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 6); - $c += 5; - break; - - } - - } - - return $utf8; - - } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { - // array, or object notation - - if ($str{0} == '[') { - $stk = array(SERVICES_JSON_IN_ARR); - $arr = array(); - } else { - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $stk = array(SERVICES_JSON_IN_OBJ); - $obj = array(); - } else { - $stk = array(SERVICES_JSON_IN_OBJ); - $obj = new stdClass(); - } - } - - array_push($stk, array('what' => SERVICES_JSON_SLICE, - 'where' => 0, - 'delim' => false)); - - $chrs = substr($str, 1, -1); - $chrs = $this->reduce_string($chrs); - - if ($chrs == '') { - if (reset($stk) == SERVICES_JSON_IN_ARR) { - return $arr; - - } else { - return $obj; - - } - } - - //print("\nparsing {$chrs}\n"); - - $strlen_chrs = strlen($chrs); - - for ($c = 0; $c <= $strlen_chrs; ++$c) { - - $top = end($stk); - $substr_chrs_c_2 = substr($chrs, $c, 2); - - if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { - // found a comma that is not inside a string, array, etc., - // OR we've reached the end of the character list - $slice = substr($chrs, $top['where'], ($c - $top['where'])); - array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); - //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - if (reset($stk) == SERVICES_JSON_IN_ARR) { - // we are in an array, so just push an element onto the stack - array_push($arr, $this->decode($slice)); - - } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { - // we are in an object, so figure - // out the property name and set an - // element in an associative array, - // for now - $parts = array(); - - if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { - // "name":value pair - $key = $this->decode($parts[1]); - $val = $this->decode($parts[2]); - - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $obj[$key] = $val; - } else { - $obj->$key = $val; - } - } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { - // name:value pair, where name is unquoted - $key = $parts[1]; - $val = $this->decode($parts[2]); - - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $obj[$key] = $val; - } else { - $obj->$key = $val; - } - } - - } - - } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { - // found a quote, and we are not inside a string - array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); - //print("Found start of string at {$c}\n"); - - } elseif (($chrs{$c} == $top['delim']) && - ($top['what'] == SERVICES_JSON_IN_STR) && - (($chrs{$c - 1} != '\\') || - ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) { - // found a quote, we're in a string, and it's not escaped - array_pop($stk); - //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); - - } elseif (($chrs{$c} == '[') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a left-bracket, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); - //print("Found start of array at {$c}\n"); - - } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { - // found a right-bracket, and we're in an array - array_pop($stk); - //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } elseif (($chrs{$c} == '{') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a left-brace, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); - //print("Found start of object at {$c}\n"); - - } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { - // found a right-brace, and we're in an object - array_pop($stk); - //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } elseif (($substr_chrs_c_2 == '/*') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a comment start, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); - $c++; - //print("Found start of comment at {$c}\n"); - - } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { - // found a comment end, and we're in one now - array_pop($stk); - $c++; - - for ($i = $top['where']; $i <= $c; ++$i) - $chrs = substr_replace($chrs, ' ', $i, 1); - - //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } - - } - - if (reset($stk) == SERVICES_JSON_IN_ARR) { - return $arr; - - } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { - return $obj; - - } - - } - } - } - - /** - * @todo Ultimately, this should just call PEAR::isError() - */ - function isError($data, $code = null) - { - if (class_exists('pear')) { - return PEAR::isError($data, $code); - } elseif (is_object($data) && (get_class($data) == 'services_json_error' || - is_subclass_of($data, 'services_json_error'))) { - return true; - } - - return false; - } + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFC00 & $bytes) == 0xD800 && strlen($utf16) >= 4 && (0xFC & ord($utf16{2})) == 0xDC: + // return a 4-byte UTF-8 character + $char = ((($bytes & 0x03FF) << 10) + | ((ord($utf16{2}) & 0x03) << 8) + | ord($utf16{3})); + $char += 0x10000; + return chr(0xF0 | (($char >> 18) & 0x07)) + . chr(0x80 | (($char >> 12) & 0x3F)) + . chr(0x80 | (($char >> 6) & 0x3F)) + . chr(0x80 | ($char & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + + case 4: + // return a UTF-16 surrogate pair from a 4-byte UTF-8 char + if(ord($utf8{0}) > 0xF4) return ''; # invalid + $char = ((0x1C0000 & (ord($utf8{0}) << 18)) + | (0x03F000 & (ord($utf8{1}) << 12)) + | (0x000FC0 & (ord($utf8{2}) << 6)) + | (0x00003F & ord($utf8{3}))); + if($char > 0x10FFFF) return ''; # invalid + $char -= 0x10000; + return chr(0xD8 | (($char >> 18) & 0x03)) + . chr(($char >> 10) & 0xFF) + . chr(0xDC | (($char >> 8) & 0x03)) + . chr($char & 0xFF); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * @param bool $pretty pretty-print output with indents and newlines + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var, $pretty=false) + { + $this->indent = 0; + $this->pretty = $pretty; + $this->nameValSeparator = $pretty ? ': ' : ':'; + return $this->encode2($var); + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access private + */ + function encode2($var) + { + if ($this->pretty) { + $close = "\n" . str_repeat("\t", $this->indent); + $open = $close . "\t"; + $mid = ',' . $open; + } + else { + $open = $close = ''; + $mid = ','; + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + // These will always return a surrogate pair + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + if($utf16 == '') { + $ascii .= '\ufffd'; + } else { + $utf16 = str_split($utf16, 2); + $ascii .= sprintf('\u%04s\u%04s', bin2hex($utf16[0]), bin2hex($utf16[1])); + } + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $this->indent++; + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + $this->indent--; + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . $open . join($mid, $properties) . $close . '}'; + } + + // treat it like a regular array + $this->indent++; + $elements = array_map(array($this, 'encode2'), $var); + $this->indent--; + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . $open . join($mid, $elements) . $close . ']'; + + case 'object': + $vars = get_object_vars($var); + + $this->indent++; + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + $this->indent--; + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . $open . join($mid, $properties) . $close . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode2($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\uD[89AB][0-9A-F]{2}\\\uD[C-F][0-9A-F]{2}/i', substr($chrs, $c, 12)): + // escaped unicode surrogate pair + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))) + . chr(hexdec(substr($chrs, ($c + 8), 2))) + . chr(hexdec(substr($chrs, ($c + 10), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 11; + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array( 'what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + (($chrs{$c - 1} != '\\') || + ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) { + // found a quote, we're in a string, and it's not escaped + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } } @@ -831,31 +831,31 @@ class Services_JSON /// @cond if (class_exists('PEAR_Error')) { - /** - * @ingroup API - */ - class Services_JSON_Error extends PEAR_Error - { - function Services_JSON_Error($message = 'unknown error', $code = null, - $mode = null, $options = null, $userinfo = null) - { - parent::PEAR_Error($message, $code, $mode, $options, $userinfo); - } - } + /** + * @ingroup API + */ + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } } else { /// @endcond - /** - * @todo Ultimately, this class shall be descended from PEAR_Error - * @ingroup API - */ - class Services_JSON_Error - { - function Services_JSON_Error($message = 'unknown error', $code = null, - $mode = null, $options = null, $userinfo = null) - { - - } - } + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + * @ingroup API + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } } diff --git a/includes/db/DatabaseIbm_db2.php b/includes/db/DatabaseIbm_db2.php index 56dc3c67b1..fcd0bc2d82 100644 --- a/includes/db/DatabaseIbm_db2.php +++ b/includes/db/DatabaseIbm_db2.php @@ -684,13 +684,13 @@ EOF; throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) ); } return $row; - } - - /** - * Override if introduced to base Database class - */ - public function initial_setup() { - // do nothing + } + + /** + * Override if introduced to base Database class + */ + public function initial_setup() { + // do nothing } /** @@ -721,12 +721,12 @@ EOF; print "
$mwe

"; } } - - /** + + /** * Escapes strings - * Doesn't escape numbers - * @param string s string to escape - * @return escaped string + * Doesn't escape numbers + * @param string s string to escape + * @return escaped string */ public function addQuotes( $s ) { //wfDebug("DB2::addQuotes($s)\n"); @@ -814,48 +814,48 @@ EOF; $s = str_replace($from, $to, $s); // DB2 expects '', not \' escaping return $s; } - - /** - * Switch into the database schema + + /** + * Switch into the database schema */ protected function applySchema() { - if ( !($this->mSchemaSet) ) { + if ( !($this->mSchemaSet) ) { $this->mSchemaSet = true; $this->begin(); - $this->doQuery("SET SCHEMA = $this->mSchema"); + $this->doQuery("SET SCHEMA = $this->mSchema"); $this->commit(); } - } - - /** - * Start a transaction (mandatory) - */ - public function begin() { + } + + /** + * Start a transaction (mandatory) + */ + public function begin() { // turn off auto-commit db2_autocommit($this->mConn, DB2_AUTOCOMMIT_OFF); - $this->mTrxLevel = 1; - } - - /** + $this->mTrxLevel = 1; + } + + /** * End a transaction - * Must have a preceding begin() - */ - public function commit() { + * Must have a preceding begin() + */ + public function commit() { db2_commit($this->mConn); // turn auto-commit back on db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON); - $this->mTrxLevel = 0; - } - - /** - * Cancel a transaction - */ - public function rollback() { + $this->mTrxLevel = 0; + } + + /** + * Cancel a transaction + */ + public function rollback() { db2_rollback($this->mConn); // turn auto-commit back on // not sure if this is appropriate db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON); - $this->mTrxLevel = 0; + $this->mTrxLevel = 0; } /** diff --git a/maintenance/ibm_db2/README b/maintenance/ibm_db2/README index bbd076f193..4a2c0f608d 100644 --- a/maintenance/ibm_db2/README +++ b/maintenance/ibm_db2/README @@ -1,41 +1,41 @@ -== Syntax differences between other databases and IBM DB2 == -{| border cellspacing=0 cellpadding=4 -!MySQL!!IBM DB2 -|- - -|SELECT 1 FROM $table LIMIT 1 -|SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST -WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema' -|- -|MySQL code tries to read one row and interprets lack of error as proof of existence. -|DB2 code counts the number of TABLES of that name in the database. There ought to be 1 for it to exist. -|- -|BEGIN -|(implicit) -|- -|TEXT -|VARCHAR(255) or CLOB -|- -|TIMESTAMPTZ -|TIMESTAMP -|- -|BYTEA -|VARGRAPHIC(255) -|- -|DEFAULT nextval('some_kind_of_sequence'), -|GENERATED ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1), -|- -|CIDR -|VARCHAR(255) -|- -|LIMIT 10 -|FETCH FIRST 10 ROWS ONLY -|- -|ROLLBACK TO -|ROLLBACK TO SAVEPOINT -|- -|RELEASE -|RELEASE SAVEPOINT -|} -== See also == +== Syntax differences between other databases and IBM DB2 == +{| border cellspacing=0 cellpadding=4 +!MySQL!!IBM DB2 +|- + +|SELECT 1 FROM $table LIMIT 1 +|SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST +WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema' +|- +|MySQL code tries to read one row and interprets lack of error as proof of existence. +|DB2 code counts the number of TABLES of that name in the database. There ought to be 1 for it to exist. +|- +|BEGIN +|(implicit) +|- +|TEXT +|VARCHAR(255) or CLOB +|- +|TIMESTAMPTZ +|TIMESTAMP +|- +|BYTEA +|VARGRAPHIC(255) +|- +|DEFAULT nextval('some_kind_of_sequence'), +|GENERATED ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1), +|- +|CIDR +|VARCHAR(255) +|- +|LIMIT 10 +|FETCH FIRST 10 ROWS ONLY +|- +|ROLLBACK TO +|ROLLBACK TO SAVEPOINT +|- +|RELEASE +|RELEASE SAVEPOINT +|} +== See also == *[http://ca.php.net/manual/en/function.db2-connect.php PHP Manual for DB2 functions] \ No newline at end of file diff --git a/maintenance/ibm_db2/tables.sql b/maintenance/ibm_db2/tables.sql index d0da55d6eb..5e91102d34 100644 --- a/maintenance/ibm_db2/tables.sql +++ b/maintenance/ibm_db2/tables.sql @@ -1,604 +1,604 @@ --- DB2 - --- SQL to create the initial tables for the MediaWiki database. --- This is read and executed by the install script; you should --- not have to run it by itself unless doing a manual install. --- This is the IBM DB2 version. --- For information about each table, please see the notes in maintenance/tables.sql --- Please make sure all dollar-quoting uses $mw$ at the start of the line --- TODO: Change CHAR/SMALLINT to BOOL (still used in a non-bool fashion in PHP code) - - - - -CREATE SEQUENCE user_user_id_seq AS INTEGER START WITH 0 INCREMENT BY 1; -CREATE TABLE mwuser ( -- replace reserved word 'user' - user_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT nextval('user_user_id_seq'), - user_name VARCHAR(255) NOT NULL UNIQUE, - user_real_name VARCHAR(255), - user_password clob(1K), - user_newpassword clob(1K), - user_newpass_time TIMESTAMP, - user_token VARCHAR(255), - user_email VARCHAR(255), - user_email_token VARCHAR(255), - user_email_token_expires TIMESTAMP, - user_email_authenticated TIMESTAMP, - user_options CLOB(64K), - user_touched TIMESTAMP, - user_registration TIMESTAMP, - user_editcount INTEGER -); -CREATE INDEX user_email_token_idx ON mwuser (user_email_token); - --- Create a dummy user to satisfy fk contraints especially with revisions -INSERT INTO mwuser - VALUES (NEXTVAL FOR user_user_id_seq,'Anonymous','', NULL,NULL,CURRENT_TIMESTAMP,NULL, NULL,NULL,NULL,NULL, NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,0); - -CREATE TABLE user_groups ( - ug_user INTEGER REFERENCES mwuser(user_id) ON DELETE CASCADE, - ug_group VARCHAR(255) NOT NULL -); -CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group); - -CREATE TABLE user_newtalk ( - user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE, - user_ip VARCHAR(255), - user_last_timestamp TIMESTAMP -); -CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id); -CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip); - - -CREATE SEQUENCE page_page_id_seq; -CREATE TABLE page ( - page_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT NEXT VALUE FOR user_user_id_seq, - page_namespace SMALLINT NOT NULL, - page_title VARCHAR(255) NOT NULL, - page_restrictions clob(1K), - page_counter BIGINT NOT NULL DEFAULT 0, - page_is_redirect SMALLINT NOT NULL DEFAULT 0, - page_is_new SMALLINT NOT NULL DEFAULT 0, - page_random NUMERIC(15,14) NOT NULL, - page_touched TIMESTAMP, - page_latest INTEGER NOT NULL, -- FK? - page_len INTEGER NOT NULL -); -CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title); ---CREATE INDEX page_main_title ON page (page_title) WHERE page_namespace = 0; ---CREATE INDEX page_talk_title ON page (page_title) WHERE page_namespace = 1; ---CREATE INDEX page_user_title ON page (page_title) WHERE page_namespace = 2; ---CREATE INDEX page_utalk_title ON page (page_title) WHERE page_namespace = 3; ---CREATE INDEX page_project_title ON page (page_title) WHERE page_namespace = 4; -CREATE INDEX page_random_idx ON page (page_random); -CREATE INDEX page_len_idx ON page (page_len); - ---CREATE FUNCTION page_deleted() RETURNS TRIGGER LANGUAGE plpgsql AS ---$mw$ ---BEGIN ---DELETE FROM recentchanges WHERE rc_namespace = OLD.page_namespace AND rc_title = OLD.page_title; ---RETURN NULL; ---END; ---$mw$; - ---CREATE TRIGGER page_deleted AFTER DELETE ON page --- FOR EACH ROW EXECUTE PROCEDURE page_deleted(); - -CREATE SEQUENCE rev_rev_id_val; -CREATE TABLE revision ( - rev_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('rev_rev_id_val'), - rev_page INTEGER REFERENCES page (page_id) ON DELETE CASCADE, - rev_text_id INTEGER, -- FK - rev_comment clob(1K), -- changed from VARCHAR(255) - rev_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE RESTRICT, - rev_user_text VARCHAR(255) NOT NULL, - rev_timestamp TIMESTAMP NOT NULL, - rev_minor_edit SMALLINT NOT NULL DEFAULT 0, - rev_deleted SMALLINT NOT NULL DEFAULT 0, - rev_len INTEGER, - rev_parent_id INTEGER -); -CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id); -CREATE INDEX rev_text_id_idx ON revision (rev_text_id); -CREATE INDEX rev_timestamp_idx ON revision (rev_timestamp); -CREATE INDEX rev_user_idx ON revision (rev_user); -CREATE INDEX rev_user_text_idx ON revision (rev_user_text); - - -CREATE SEQUENCE text_old_id_val; -CREATE TABLE pagecontent ( -- replaces reserved word 'text' - old_id INTEGER NOT NULL, - --PRIMARY KEY DEFAULT nextval('text_old_id_val'), - old_text CLOB(16M), - old_flags clob(1K) -); - -CREATE SEQUENCE pr_id_val; -CREATE TABLE page_restrictions ( - pr_id INTEGER NOT NULL UNIQUE, - --DEFAULT nextval('pr_id_val'), - pr_page INTEGER NOT NULL - --(used to be nullable) - REFERENCES page (page_id) ON DELETE CASCADE, - pr_type VARCHAR(255) NOT NULL, - pr_level VARCHAR(255) NOT NULL, - pr_cascade SMALLINT NOT NULL, - pr_user INTEGER, - pr_expiry TIMESTAMP, - PRIMARY KEY (pr_page, pr_type) -); ---ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type); - -CREATE TABLE page_props ( - pp_page INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE, - pp_propname VARCHAR(255) NOT NULL, - pp_value CLOB(64K) NOT NULL, - PRIMARY KEY (pp_page,pp_propname) -); ---ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname); -CREATE INDEX page_props_propname ON page_props (pp_propname); - - - -CREATE TABLE archive ( - ar_namespace SMALLINT NOT NULL, - ar_title VARCHAR(255) NOT NULL, - ar_text CLOB(16M), - ar_page_id INTEGER, - ar_parent_id INTEGER, - ar_comment clob(1K), - ar_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - ar_user_text VARCHAR(255) NOT NULL, - ar_timestamp TIMESTAMP NOT NULL, - ar_minor_edit SMALLINT NOT NULL DEFAULT 0, - ar_flags clob(1K), - ar_rev_id INTEGER, - ar_text_id INTEGER, - ar_deleted SMALLINT NOT NULL DEFAULT 0, - ar_len INTEGER -); -CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp); -CREATE INDEX archive_user_text ON archive (ar_user_text); - - - -CREATE TABLE redirect ( - rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, - rd_namespace SMALLINT NOT NULL, - rd_title VARCHAR(255) NOT NULL -); -CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from); - - -CREATE TABLE pagelinks ( - pl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, - pl_namespace SMALLINT NOT NULL, - pl_title VARCHAR(255) NOT NULL -); -CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title); - -CREATE TABLE templatelinks ( - tl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, - tl_namespace SMALLINT NOT NULL, - tl_title VARCHAR(255) NOT NULL -); -CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from); - -CREATE TABLE imagelinks ( - il_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, - il_to VARCHAR(255) NOT NULL -); -CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from); - -CREATE TABLE categorylinks ( - cl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, - cl_to VARCHAR(255) NOT NULL, - cl_sortkey VARCHAR(255), - cl_timestamp TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to); -CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from); - - - -CREATE TABLE externallinks ( - el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, - el_to VARCHAR(255) NOT NULL, - el_index VARCHAR(255) NOT NULL -); -CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to); -CREATE INDEX externallinks_index ON externallinks (el_index); - -CREATE TABLE langlinks ( - ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE, - ll_lang VARCHAR(255), - ll_title VARCHAR(255) -); -CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang); -CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title); - - -CREATE TABLE site_stats ( - ss_row_id INTEGER NOT NULL UNIQUE, - ss_total_views INTEGER DEFAULT 0, - ss_total_edits INTEGER DEFAULT 0, - ss_good_articles INTEGER DEFAULT 0, - ss_total_pages INTEGER DEFAULT -1, - ss_users INTEGER DEFAULT -1, - ss_admins INTEGER DEFAULT -1, - ss_images INTEGER DEFAULT 0 -); - -CREATE TABLE hitcounter ( - hc_id BIGINT NOT NULL -); - -CREATE SEQUENCE ipblocks_ipb_id_val; -CREATE TABLE ipblocks ( - ipb_id INTEGER NOT NULL PRIMARY KEY, - --DEFAULT nextval('ipblocks_ipb_id_val'), - ipb_address VARCHAR(255), - ipb_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - ipb_by INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE, - ipb_by_text VARCHAR(255) NOT NULL DEFAULT '', - ipb_reason VARCHAR(255) NOT NULL, - ipb_timestamp TIMESTAMP NOT NULL, - ipb_auto SMALLINT NOT NULL DEFAULT 0, - ipb_anon_only SMALLINT NOT NULL DEFAULT 0, - ipb_create_account SMALLINT NOT NULL DEFAULT 1, - ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1, - ipb_expiry TIMESTAMP NOT NULL, - ipb_range_start VARCHAR(255), - ipb_range_end VARCHAR(255), - ipb_deleted SMALLINT NOT NULL DEFAULT 0, - ipb_block_email SMALLINT NOT NULL DEFAULT 0 - -); -CREATE INDEX ipb_address ON ipblocks (ipb_address); -CREATE INDEX ipb_user ON ipblocks (ipb_user); -CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end); - - - -CREATE TABLE image ( - img_name VARCHAR(255) NOT NULL PRIMARY KEY, - img_size INTEGER NOT NULL, - img_width INTEGER NOT NULL, - img_height INTEGER NOT NULL, - img_metadata CLOB(16M) NOT NULL DEFAULT '', - img_bits SMALLINT, - img_media_type VARCHAR(255), - img_major_mime VARCHAR(255) DEFAULT 'unknown', - img_minor_mime VARCHAR(255) DEFAULT 'unknown', - img_description clob(1K) NOT NULL DEFAULT '', - img_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - img_user_text VARCHAR(255) NOT NULL DEFAULT '', - img_timestamp TIMESTAMP, - img_sha1 VARCHAR(255) NOT NULL DEFAULT '' -); -CREATE INDEX img_size_idx ON image (img_size); -CREATE INDEX img_timestamp_idx ON image (img_timestamp); -CREATE INDEX img_sha1 ON image (img_sha1); - -CREATE TABLE oldimage ( - oi_name VARCHAR(255) NOT NULL, - oi_archive_name VARCHAR(255) NOT NULL, - oi_size INTEGER NOT NULL, - oi_width INTEGER NOT NULL, - oi_height INTEGER NOT NULL, - oi_bits SMALLINT NOT NULL, - oi_description clob(1K), - oi_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - oi_user_text VARCHAR(255) NOT NULL, - oi_timestamp TIMESTAMP NOT NULL, - oi_metadata CLOB(16M) NOT NULL DEFAULT '', - oi_media_type VARCHAR(255) , - oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', - oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', - oi_deleted SMALLINT NOT NULL DEFAULT 0, - oi_sha1 VARCHAR(255) NOT NULL DEFAULT '', - FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE -); ---ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascade FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE; -CREATE INDEX oi_name_timestamp ON oldimage (oi_name,oi_timestamp); -CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name); -CREATE INDEX oi_sha1 ON oldimage (oi_sha1); - - -CREATE SEQUENCE filearchive_fa_id_seq; -CREATE TABLE filearchive ( - fa_id INTEGER NOT NULL PRIMARY KEY, - --PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'), - fa_name VARCHAR(255) NOT NULL, - fa_archive_name VARCHAR(255), - fa_storage_group VARCHAR(255), - fa_storage_key VARCHAR(255), - fa_deleted_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - fa_deleted_timestamp TIMESTAMP NOT NULL, - fa_deleted_reason VARCHAR(255), - fa_size INTEGER NOT NULL, - fa_width INTEGER NOT NULL, - fa_height INTEGER NOT NULL, - fa_metadata CLOB(16M) NOT NULL DEFAULT '', - fa_bits SMALLINT, - fa_media_type VARCHAR(255), - fa_major_mime VARCHAR(255) DEFAULT 'unknown', - fa_minor_mime VARCHAR(255) DEFAULT 'unknown', - fa_description clob(1K) NOT NULL, - fa_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - fa_user_text VARCHAR(255) NOT NULL, - fa_timestamp TIMESTAMP, - fa_deleted SMALLINT NOT NULL DEFAULT 0 -); -CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp); -CREATE INDEX fa_dupe ON filearchive (fa_storage_group, fa_storage_key); -CREATE INDEX fa_notime ON filearchive (fa_deleted_timestamp); -CREATE INDEX fa_nouser ON filearchive (fa_deleted_user); - -CREATE SEQUENCE rc_rc_id_seq; -CREATE TABLE recentchanges ( - rc_id INTEGER NOT NULL PRIMARY KEY, - --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'), - rc_timestamp TIMESTAMP NOT NULL, - rc_cur_time TIMESTAMP NOT NULL, - rc_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - rc_user_text VARCHAR(255) NOT NULL, - rc_namespace SMALLINT NOT NULL, - rc_title VARCHAR(255) NOT NULL, - rc_comment VARCHAR(255), - rc_minor SMALLINT NOT NULL DEFAULT 0, - rc_bot SMALLINT NOT NULL DEFAULT 0, - rc_new SMALLINT NOT NULL DEFAULT 0, - rc_cur_id INTEGER REFERENCES page(page_id) ON DELETE SET NULL, - rc_this_oldid INTEGER NOT NULL, - rc_last_oldid INTEGER NOT NULL, - rc_type SMALLINT NOT NULL DEFAULT 0, - rc_moved_to_ns SMALLINT, - rc_moved_to_title VARCHAR(255), - rc_patrolled SMALLINT NOT NULL DEFAULT 0, - rc_ip VARCHAR(255), -- was CIDR type - rc_old_len INTEGER, - rc_new_len INTEGER, - rc_deleted SMALLINT NOT NULL DEFAULT 0, - rc_logid INTEGER NOT NULL DEFAULT 0, - rc_log_type VARCHAR(255), - rc_log_action VARCHAR(255), - rc_params CLOB(64K) -); -CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp); -CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title); -CREATE INDEX rc_cur_id ON recentchanges (rc_cur_id); -CREATE INDEX new_name_timestamp ON recentchanges (rc_new, rc_namespace, rc_timestamp); -CREATE INDEX rc_ip ON recentchanges (rc_ip); - - - -CREATE TABLE watchlist ( - wl_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE, - wl_namespace SMALLINT NOT NULL DEFAULT 0, - wl_title VARCHAR(255) NOT NULL, - wl_notificationtimestamp TIMESTAMP -); -CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user); - - -CREATE TABLE math ( - math_inputhash VARGRAPHIC(255) NOT NULL UNIQUE, - math_outputhash VARGRAPHIC(255) NOT NULL, - math_html_conservativeness SMALLINT NOT NULL, - math_html VARCHAR(255), - math_mathml VARCHAR(255) -); - - -CREATE TABLE interwiki ( - iw_prefix VARCHAR(255) NOT NULL UNIQUE, - iw_url CLOB(64K) NOT NULL, - iw_local SMALLINT NOT NULL, - iw_trans SMALLINT NOT NULL DEFAULT 0 -); - - -CREATE TABLE querycache ( - qc_type VARCHAR(255) NOT NULL, - qc_value INTEGER NOT NULL, - qc_namespace SMALLINT NOT NULL, - qc_title VARCHAR(255) NOT NULL -); -CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value); - - - -CREATE TABLE querycache_info ( - qci_type VARCHAR(255) UNIQUE NOT NULL, - qci_timestamp TIMESTAMP -); - - -CREATE TABLE querycachetwo ( - qcc_type VARCHAR(255) NOT NULL, - qcc_value INTEGER NOT NULL DEFAULT 0, - qcc_namespace INTEGER NOT NULL DEFAULT 0, - qcc_title VARCHAR(255) NOT NULL DEFAULT '', - qcc_namespacetwo INTEGER NOT NULL DEFAULT 0, - qcc_titletwo VARCHAR(255) NOT NULL DEFAULT '' -); -CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value); -CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title); -CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo); - -CREATE TABLE objectcache ( - keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable - value CLOB(16M) NOT NULL DEFAULT '', - exptime TIMESTAMP NOT NULL -); -CREATE INDEX objectcacache_exptime ON objectcache (exptime); - - - -CREATE TABLE transcache ( - tc_url VARCHAR(255) NOT NULL UNIQUE, - tc_contents VARCHAR(255) NOT NULL, - tc_time TIMESTAMP NOT NULL -); - -CREATE SEQUENCE log_log_id_seq; -CREATE TABLE logging ( - log_id INTEGER NOT NULL PRIMARY KEY, - --PRIMARY KEY DEFAULT nextval('log_log_id_seq'), - log_type VARCHAR(255) NOT NULL, - log_action VARCHAR(255) NOT NULL, - log_timestamp TIMESTAMP NOT NULL, - log_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - log_namespace SMALLINT NOT NULL, - log_title VARCHAR(255) NOT NULL, - log_comment VARCHAR(255), - log_params CLOB(64K), - log_deleted SMALLINT NOT NULL DEFAULT 0 -); -CREATE INDEX logging_type_name ON logging (log_type, log_timestamp); -CREATE INDEX logging_user_time ON logging (log_timestamp, log_user); -CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp); - -CREATE SEQUENCE trackbacks_tb_id_seq; -CREATE TABLE trackbacks ( - tb_id INTEGER NOT NULL PRIMARY KEY, - --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'), - tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE, - tb_title VARCHAR(255) NOT NULL, - tb_url CLOB(64K) NOT NULL, - tb_ex VARCHAR(255), - tb_name VARCHAR(255) -); -CREATE INDEX trackback_page ON trackbacks (tb_page); - - -CREATE SEQUENCE job_job_id_seq; -CREATE TABLE job ( - job_id INTEGER NOT NULL PRIMARY KEY, - --PRIMARY KEY DEFAULT nextval('job_job_id_seq'), - job_cmd VARCHAR(255) NOT NULL, - job_namespace SMALLINT NOT NULL, - job_title VARCHAR(255) NOT NULL, - job_params CLOB(64K) NOT NULL -); -CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title); - - - --- Postgres' Tsearch2 dropped ---ALTER TABLE page ADD titlevector tsvector; ---CREATE FUNCTION ts2_page_title() RETURNS TRIGGER LANGUAGE plpgsql AS ---$mw$ ---BEGIN ---IF TG_OP = 'INSERT' THEN --- NEW.titlevector = to_tsvector('default',REPLACE(NEW.page_title,'/',' ')); ---ELSIF NEW.page_title != OLD.page_title THEN --- NEW.titlevector := to_tsvector('default',REPLACE(NEW.page_title,'/',' ')); ---END IF; ---RETURN NEW; ---END; ---$mw$; - ---CREATE TRIGGER ts2_page_title BEFORE INSERT OR UPDATE ON page --- FOR EACH ROW EXECUTE PROCEDURE ts2_page_title(); - - ---ALTER TABLE pagecontent ADD textvector tsvector; ---CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS ---$mw$ ---BEGIN ---IF TG_OP = 'INSERT' THEN --- NEW.textvector = to_tsvector('default',NEW.old_text); ---ELSIF NEW.old_text != OLD.old_text THEN --- NEW.textvector := to_tsvector('default',NEW.old_text); ---END IF; ---RETURN NEW; ---END; ---$mw$; - ---CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent --- FOR EACH ROW EXECUTE PROCEDURE ts2_page_text(); - --- These are added by the setup script due to version compatibility issues --- If using 8.1, we switch from "gin" to "gist" - ---CREATE INDEX ts2_page_title ON page USING gin(titlevector); ---CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector); - ---TODO ---CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS ---$mw$ --- INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3); --- SELECT 1; ---$mw$; - --- hack implementation --- should be replaced with OmniFind, Contains(), etc -CREATE TABLE searchindex ( - si_page int NOT NULL, - si_title varchar(255) NOT NULL default '', - si_text clob NOT NULL -); - --- This table is not used unless profiling is turned on -CREATE TABLE profiling ( - pf_count INTEGER NOT NULL DEFAULT 0, - pf_time NUMERIC(18,10) NOT NULL DEFAULT 0, - pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0, - pf_name VARCHAR(255) NOT NULL, - pf_server VARCHAR(255) -); -CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server); - -CREATE TABLE protected_titles ( - pt_namespace SMALLINT NOT NULL, - pt_title VARCHAR(255) NOT NULL, - pt_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, - pt_reason clob(1K), - pt_timestamp TIMESTAMP NOT NULL, - pt_expiry TIMESTAMP , - pt_create_perm VARCHAR(255) NOT NULL DEFAULT '' -); -CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title); - - - -CREATE TABLE updatelog ( - ul_key VARCHAR(255) NOT NULL PRIMARY KEY -); - -CREATE SEQUENCE category_id_seq; -CREATE TABLE category ( - cat_id INTEGER NOT NULL PRIMARY KEY, - --PRIMARY KEY DEFAULT nextval('category_id_seq'), - cat_title VARCHAR(255) NOT NULL, - cat_pages INTEGER NOT NULL DEFAULT 0, - cat_subcats INTEGER NOT NULL DEFAULT 0, - cat_files INTEGER NOT NULL DEFAULT 0, - cat_hidden SMALLINT NOT NULL DEFAULT 0 -); -CREATE UNIQUE INDEX category_title ON category(cat_title); -CREATE INDEX category_pages ON category(cat_pages); - -CREATE TABLE mediawiki_version ( - type VARCHAR(255) NOT NULL, - mw_version VARCHAR(255) NOT NULL, - notes VARCHAR(255) , - - pg_version VARCHAR(255) , - pg_dbname VARCHAR(255) , - pg_user VARCHAR(255) , - pg_port VARCHAR(255) , - mw_schema VARCHAR(255) , - ts2_schema VARCHAR(255) , - ctype VARCHAR(255) , - - sql_version VARCHAR(255) , - sql_date VARCHAR(255) , - cdate TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP -); - -INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date) - VALUES ('Creation','??','$LastChangedRevision: 34049 $','$LastChangedDate: 2008-04-30 10:20:36 -0400 (Wed, 30 Apr 2008) $'); - +-- DB2 + +-- SQL to create the initial tables for the MediaWiki database. +-- This is read and executed by the install script; you should +-- not have to run it by itself unless doing a manual install. +-- This is the IBM DB2 version. +-- For information about each table, please see the notes in maintenance/tables.sql +-- Please make sure all dollar-quoting uses $mw$ at the start of the line +-- TODO: Change CHAR/SMALLINT to BOOL (still used in a non-bool fashion in PHP code) + + + + +CREATE SEQUENCE user_user_id_seq AS INTEGER START WITH 0 INCREMENT BY 1; +CREATE TABLE mwuser ( -- replace reserved word 'user' + user_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT nextval('user_user_id_seq'), + user_name VARCHAR(255) NOT NULL UNIQUE, + user_real_name VARCHAR(255), + user_password clob(1K), + user_newpassword clob(1K), + user_newpass_time TIMESTAMP, + user_token VARCHAR(255), + user_email VARCHAR(255), + user_email_token VARCHAR(255), + user_email_token_expires TIMESTAMP, + user_email_authenticated TIMESTAMP, + user_options CLOB(64K), + user_touched TIMESTAMP, + user_registration TIMESTAMP, + user_editcount INTEGER +); +CREATE INDEX user_email_token_idx ON mwuser (user_email_token); + +-- Create a dummy user to satisfy fk contraints especially with revisions +INSERT INTO mwuser + VALUES (NEXTVAL FOR user_user_id_seq,'Anonymous','', NULL,NULL,CURRENT_TIMESTAMP,NULL, NULL,NULL,NULL,NULL, NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,0); + +CREATE TABLE user_groups ( + ug_user INTEGER REFERENCES mwuser(user_id) ON DELETE CASCADE, + ug_group VARCHAR(255) NOT NULL +); +CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group); + +CREATE TABLE user_newtalk ( + user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE, + user_ip VARCHAR(255), + user_last_timestamp TIMESTAMP +); +CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id); +CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip); + + +CREATE SEQUENCE page_page_id_seq; +CREATE TABLE page ( + page_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT NEXT VALUE FOR user_user_id_seq, + page_namespace SMALLINT NOT NULL, + page_title VARCHAR(255) NOT NULL, + page_restrictions clob(1K), + page_counter BIGINT NOT NULL DEFAULT 0, + page_is_redirect SMALLINT NOT NULL DEFAULT 0, + page_is_new SMALLINT NOT NULL DEFAULT 0, + page_random NUMERIC(15,14) NOT NULL, + page_touched TIMESTAMP, + page_latest INTEGER NOT NULL, -- FK? + page_len INTEGER NOT NULL +); +CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title); +--CREATE INDEX page_main_title ON page (page_title) WHERE page_namespace = 0; +--CREATE INDEX page_talk_title ON page (page_title) WHERE page_namespace = 1; +--CREATE INDEX page_user_title ON page (page_title) WHERE page_namespace = 2; +--CREATE INDEX page_utalk_title ON page (page_title) WHERE page_namespace = 3; +--CREATE INDEX page_project_title ON page (page_title) WHERE page_namespace = 4; +CREATE INDEX page_random_idx ON page (page_random); +CREATE INDEX page_len_idx ON page (page_len); + +--CREATE FUNCTION page_deleted() RETURNS TRIGGER LANGUAGE plpgsql AS +--$mw$ +--BEGIN +--DELETE FROM recentchanges WHERE rc_namespace = OLD.page_namespace AND rc_title = OLD.page_title; +--RETURN NULL; +--END; +--$mw$; + +--CREATE TRIGGER page_deleted AFTER DELETE ON page +-- FOR EACH ROW EXECUTE PROCEDURE page_deleted(); + +CREATE SEQUENCE rev_rev_id_val; +CREATE TABLE revision ( + rev_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('rev_rev_id_val'), + rev_page INTEGER REFERENCES page (page_id) ON DELETE CASCADE, + rev_text_id INTEGER, -- FK + rev_comment clob(1K), -- changed from VARCHAR(255) + rev_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE RESTRICT, + rev_user_text VARCHAR(255) NOT NULL, + rev_timestamp TIMESTAMP NOT NULL, + rev_minor_edit SMALLINT NOT NULL DEFAULT 0, + rev_deleted SMALLINT NOT NULL DEFAULT 0, + rev_len INTEGER, + rev_parent_id INTEGER +); +CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id); +CREATE INDEX rev_text_id_idx ON revision (rev_text_id); +CREATE INDEX rev_timestamp_idx ON revision (rev_timestamp); +CREATE INDEX rev_user_idx ON revision (rev_user); +CREATE INDEX rev_user_text_idx ON revision (rev_user_text); + + +CREATE SEQUENCE text_old_id_val; +CREATE TABLE pagecontent ( -- replaces reserved word 'text' + old_id INTEGER NOT NULL, + --PRIMARY KEY DEFAULT nextval('text_old_id_val'), + old_text CLOB(16M), + old_flags clob(1K) +); + +CREATE SEQUENCE pr_id_val; +CREATE TABLE page_restrictions ( + pr_id INTEGER NOT NULL UNIQUE, + --DEFAULT nextval('pr_id_val'), + pr_page INTEGER NOT NULL + --(used to be nullable) + REFERENCES page (page_id) ON DELETE CASCADE, + pr_type VARCHAR(255) NOT NULL, + pr_level VARCHAR(255) NOT NULL, + pr_cascade SMALLINT NOT NULL, + pr_user INTEGER, + pr_expiry TIMESTAMP, + PRIMARY KEY (pr_page, pr_type) +); +--ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type); + +CREATE TABLE page_props ( + pp_page INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE, + pp_propname VARCHAR(255) NOT NULL, + pp_value CLOB(64K) NOT NULL, + PRIMARY KEY (pp_page,pp_propname) +); +--ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname); +CREATE INDEX page_props_propname ON page_props (pp_propname); + + + +CREATE TABLE archive ( + ar_namespace SMALLINT NOT NULL, + ar_title VARCHAR(255) NOT NULL, + ar_text CLOB(16M), + ar_page_id INTEGER, + ar_parent_id INTEGER, + ar_comment clob(1K), + ar_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + ar_user_text VARCHAR(255) NOT NULL, + ar_timestamp TIMESTAMP NOT NULL, + ar_minor_edit SMALLINT NOT NULL DEFAULT 0, + ar_flags clob(1K), + ar_rev_id INTEGER, + ar_text_id INTEGER, + ar_deleted SMALLINT NOT NULL DEFAULT 0, + ar_len INTEGER +); +CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp); +CREATE INDEX archive_user_text ON archive (ar_user_text); + + + +CREATE TABLE redirect ( + rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + rd_namespace SMALLINT NOT NULL, + rd_title VARCHAR(255) NOT NULL +); +CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from); + + +CREATE TABLE pagelinks ( + pl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + pl_namespace SMALLINT NOT NULL, + pl_title VARCHAR(255) NOT NULL +); +CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title); + +CREATE TABLE templatelinks ( + tl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + tl_namespace SMALLINT NOT NULL, + tl_title VARCHAR(255) NOT NULL +); +CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from); + +CREATE TABLE imagelinks ( + il_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + il_to VARCHAR(255) NOT NULL +); +CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from); + +CREATE TABLE categorylinks ( + cl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + cl_to VARCHAR(255) NOT NULL, + cl_sortkey VARCHAR(255), + cl_timestamp TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to); +CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from); + + + +CREATE TABLE externallinks ( + el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + el_to VARCHAR(255) NOT NULL, + el_index VARCHAR(255) NOT NULL +); +CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to); +CREATE INDEX externallinks_index ON externallinks (el_index); + +CREATE TABLE langlinks ( + ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE, + ll_lang VARCHAR(255), + ll_title VARCHAR(255) +); +CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang); +CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title); + + +CREATE TABLE site_stats ( + ss_row_id INTEGER NOT NULL UNIQUE, + ss_total_views INTEGER DEFAULT 0, + ss_total_edits INTEGER DEFAULT 0, + ss_good_articles INTEGER DEFAULT 0, + ss_total_pages INTEGER DEFAULT -1, + ss_users INTEGER DEFAULT -1, + ss_admins INTEGER DEFAULT -1, + ss_images INTEGER DEFAULT 0 +); + +CREATE TABLE hitcounter ( + hc_id BIGINT NOT NULL +); + +CREATE SEQUENCE ipblocks_ipb_id_val; +CREATE TABLE ipblocks ( + ipb_id INTEGER NOT NULL PRIMARY KEY, + --DEFAULT nextval('ipblocks_ipb_id_val'), + ipb_address VARCHAR(255), + ipb_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + ipb_by INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE, + ipb_by_text VARCHAR(255) NOT NULL DEFAULT '', + ipb_reason VARCHAR(255) NOT NULL, + ipb_timestamp TIMESTAMP NOT NULL, + ipb_auto SMALLINT NOT NULL DEFAULT 0, + ipb_anon_only SMALLINT NOT NULL DEFAULT 0, + ipb_create_account SMALLINT NOT NULL DEFAULT 1, + ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1, + ipb_expiry TIMESTAMP NOT NULL, + ipb_range_start VARCHAR(255), + ipb_range_end VARCHAR(255), + ipb_deleted SMALLINT NOT NULL DEFAULT 0, + ipb_block_email SMALLINT NOT NULL DEFAULT 0 + +); +CREATE INDEX ipb_address ON ipblocks (ipb_address); +CREATE INDEX ipb_user ON ipblocks (ipb_user); +CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end); + + + +CREATE TABLE image ( + img_name VARCHAR(255) NOT NULL PRIMARY KEY, + img_size INTEGER NOT NULL, + img_width INTEGER NOT NULL, + img_height INTEGER NOT NULL, + img_metadata CLOB(16M) NOT NULL DEFAULT '', + img_bits SMALLINT, + img_media_type VARCHAR(255), + img_major_mime VARCHAR(255) DEFAULT 'unknown', + img_minor_mime VARCHAR(255) DEFAULT 'unknown', + img_description clob(1K) NOT NULL DEFAULT '', + img_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + img_user_text VARCHAR(255) NOT NULL DEFAULT '', + img_timestamp TIMESTAMP, + img_sha1 VARCHAR(255) NOT NULL DEFAULT '' +); +CREATE INDEX img_size_idx ON image (img_size); +CREATE INDEX img_timestamp_idx ON image (img_timestamp); +CREATE INDEX img_sha1 ON image (img_sha1); + +CREATE TABLE oldimage ( + oi_name VARCHAR(255) NOT NULL, + oi_archive_name VARCHAR(255) NOT NULL, + oi_size INTEGER NOT NULL, + oi_width INTEGER NOT NULL, + oi_height INTEGER NOT NULL, + oi_bits SMALLINT NOT NULL, + oi_description clob(1K), + oi_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + oi_user_text VARCHAR(255) NOT NULL, + oi_timestamp TIMESTAMP NOT NULL, + oi_metadata CLOB(16M) NOT NULL DEFAULT '', + oi_media_type VARCHAR(255) , + oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', + oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', + oi_deleted SMALLINT NOT NULL DEFAULT 0, + oi_sha1 VARCHAR(255) NOT NULL DEFAULT '', + FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE +); +--ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascade FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE; +CREATE INDEX oi_name_timestamp ON oldimage (oi_name,oi_timestamp); +CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name); +CREATE INDEX oi_sha1 ON oldimage (oi_sha1); + + +CREATE SEQUENCE filearchive_fa_id_seq; +CREATE TABLE filearchive ( + fa_id INTEGER NOT NULL PRIMARY KEY, + --PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'), + fa_name VARCHAR(255) NOT NULL, + fa_archive_name VARCHAR(255), + fa_storage_group VARCHAR(255), + fa_storage_key VARCHAR(255), + fa_deleted_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + fa_deleted_timestamp TIMESTAMP NOT NULL, + fa_deleted_reason VARCHAR(255), + fa_size INTEGER NOT NULL, + fa_width INTEGER NOT NULL, + fa_height INTEGER NOT NULL, + fa_metadata CLOB(16M) NOT NULL DEFAULT '', + fa_bits SMALLINT, + fa_media_type VARCHAR(255), + fa_major_mime VARCHAR(255) DEFAULT 'unknown', + fa_minor_mime VARCHAR(255) DEFAULT 'unknown', + fa_description clob(1K) NOT NULL, + fa_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + fa_user_text VARCHAR(255) NOT NULL, + fa_timestamp TIMESTAMP, + fa_deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp); +CREATE INDEX fa_dupe ON filearchive (fa_storage_group, fa_storage_key); +CREATE INDEX fa_notime ON filearchive (fa_deleted_timestamp); +CREATE INDEX fa_nouser ON filearchive (fa_deleted_user); + +CREATE SEQUENCE rc_rc_id_seq; +CREATE TABLE recentchanges ( + rc_id INTEGER NOT NULL PRIMARY KEY, + --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'), + rc_timestamp TIMESTAMP NOT NULL, + rc_cur_time TIMESTAMP NOT NULL, + rc_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + rc_user_text VARCHAR(255) NOT NULL, + rc_namespace SMALLINT NOT NULL, + rc_title VARCHAR(255) NOT NULL, + rc_comment VARCHAR(255), + rc_minor SMALLINT NOT NULL DEFAULT 0, + rc_bot SMALLINT NOT NULL DEFAULT 0, + rc_new SMALLINT NOT NULL DEFAULT 0, + rc_cur_id INTEGER REFERENCES page(page_id) ON DELETE SET NULL, + rc_this_oldid INTEGER NOT NULL, + rc_last_oldid INTEGER NOT NULL, + rc_type SMALLINT NOT NULL DEFAULT 0, + rc_moved_to_ns SMALLINT, + rc_moved_to_title VARCHAR(255), + rc_patrolled SMALLINT NOT NULL DEFAULT 0, + rc_ip VARCHAR(255), -- was CIDR type + rc_old_len INTEGER, + rc_new_len INTEGER, + rc_deleted SMALLINT NOT NULL DEFAULT 0, + rc_logid INTEGER NOT NULL DEFAULT 0, + rc_log_type VARCHAR(255), + rc_log_action VARCHAR(255), + rc_params CLOB(64K) +); +CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp); +CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title); +CREATE INDEX rc_cur_id ON recentchanges (rc_cur_id); +CREATE INDEX new_name_timestamp ON recentchanges (rc_new, rc_namespace, rc_timestamp); +CREATE INDEX rc_ip ON recentchanges (rc_ip); + + + +CREATE TABLE watchlist ( + wl_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE, + wl_namespace SMALLINT NOT NULL DEFAULT 0, + wl_title VARCHAR(255) NOT NULL, + wl_notificationtimestamp TIMESTAMP +); +CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user); + + +CREATE TABLE math ( + math_inputhash VARGRAPHIC(255) NOT NULL UNIQUE, + math_outputhash VARGRAPHIC(255) NOT NULL, + math_html_conservativeness SMALLINT NOT NULL, + math_html VARCHAR(255), + math_mathml VARCHAR(255) +); + + +CREATE TABLE interwiki ( + iw_prefix VARCHAR(255) NOT NULL UNIQUE, + iw_url CLOB(64K) NOT NULL, + iw_local SMALLINT NOT NULL, + iw_trans SMALLINT NOT NULL DEFAULT 0 +); + + +CREATE TABLE querycache ( + qc_type VARCHAR(255) NOT NULL, + qc_value INTEGER NOT NULL, + qc_namespace SMALLINT NOT NULL, + qc_title VARCHAR(255) NOT NULL +); +CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value); + + + +CREATE TABLE querycache_info ( + qci_type VARCHAR(255) UNIQUE NOT NULL, + qci_timestamp TIMESTAMP +); + + +CREATE TABLE querycachetwo ( + qcc_type VARCHAR(255) NOT NULL, + qcc_value INTEGER NOT NULL DEFAULT 0, + qcc_namespace INTEGER NOT NULL DEFAULT 0, + qcc_title VARCHAR(255) NOT NULL DEFAULT '', + qcc_namespacetwo INTEGER NOT NULL DEFAULT 0, + qcc_titletwo VARCHAR(255) NOT NULL DEFAULT '' +); +CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value); +CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title); +CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo); + +CREATE TABLE objectcache ( + keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable + value CLOB(16M) NOT NULL DEFAULT '', + exptime TIMESTAMP NOT NULL +); +CREATE INDEX objectcacache_exptime ON objectcache (exptime); + + + +CREATE TABLE transcache ( + tc_url VARCHAR(255) NOT NULL UNIQUE, + tc_contents VARCHAR(255) NOT NULL, + tc_time TIMESTAMP NOT NULL +); + +CREATE SEQUENCE log_log_id_seq; +CREATE TABLE logging ( + log_id INTEGER NOT NULL PRIMARY KEY, + --PRIMARY KEY DEFAULT nextval('log_log_id_seq'), + log_type VARCHAR(255) NOT NULL, + log_action VARCHAR(255) NOT NULL, + log_timestamp TIMESTAMP NOT NULL, + log_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + log_namespace SMALLINT NOT NULL, + log_title VARCHAR(255) NOT NULL, + log_comment VARCHAR(255), + log_params CLOB(64K), + log_deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX logging_type_name ON logging (log_type, log_timestamp); +CREATE INDEX logging_user_time ON logging (log_timestamp, log_user); +CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp); + +CREATE SEQUENCE trackbacks_tb_id_seq; +CREATE TABLE trackbacks ( + tb_id INTEGER NOT NULL PRIMARY KEY, + --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'), + tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE, + tb_title VARCHAR(255) NOT NULL, + tb_url CLOB(64K) NOT NULL, + tb_ex VARCHAR(255), + tb_name VARCHAR(255) +); +CREATE INDEX trackback_page ON trackbacks (tb_page); + + +CREATE SEQUENCE job_job_id_seq; +CREATE TABLE job ( + job_id INTEGER NOT NULL PRIMARY KEY, + --PRIMARY KEY DEFAULT nextval('job_job_id_seq'), + job_cmd VARCHAR(255) NOT NULL, + job_namespace SMALLINT NOT NULL, + job_title VARCHAR(255) NOT NULL, + job_params CLOB(64K) NOT NULL +); +CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title); + + + +-- Postgres' Tsearch2 dropped +--ALTER TABLE page ADD titlevector tsvector; +--CREATE FUNCTION ts2_page_title() RETURNS TRIGGER LANGUAGE plpgsql AS +--$mw$ +--BEGIN +--IF TG_OP = 'INSERT' THEN +-- NEW.titlevector = to_tsvector('default',REPLACE(NEW.page_title,'/',' ')); +--ELSIF NEW.page_title != OLD.page_title THEN +-- NEW.titlevector := to_tsvector('default',REPLACE(NEW.page_title,'/',' ')); +--END IF; +--RETURN NEW; +--END; +--$mw$; + +--CREATE TRIGGER ts2_page_title BEFORE INSERT OR UPDATE ON page +-- FOR EACH ROW EXECUTE PROCEDURE ts2_page_title(); + + +--ALTER TABLE pagecontent ADD textvector tsvector; +--CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS +--$mw$ +--BEGIN +--IF TG_OP = 'INSERT' THEN +-- NEW.textvector = to_tsvector('default',NEW.old_text); +--ELSIF NEW.old_text != OLD.old_text THEN +-- NEW.textvector := to_tsvector('default',NEW.old_text); +--END IF; +--RETURN NEW; +--END; +--$mw$; + +--CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent +-- FOR EACH ROW EXECUTE PROCEDURE ts2_page_text(); + +-- These are added by the setup script due to version compatibility issues +-- If using 8.1, we switch from "gin" to "gist" + +--CREATE INDEX ts2_page_title ON page USING gin(titlevector); +--CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector); + +--TODO +--CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS +--$mw$ +-- INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3); +-- SELECT 1; +--$mw$; + +-- hack implementation +-- should be replaced with OmniFind, Contains(), etc +CREATE TABLE searchindex ( + si_page int NOT NULL, + si_title varchar(255) NOT NULL default '', + si_text clob NOT NULL +); + +-- This table is not used unless profiling is turned on +CREATE TABLE profiling ( + pf_count INTEGER NOT NULL DEFAULT 0, + pf_time NUMERIC(18,10) NOT NULL DEFAULT 0, + pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0, + pf_name VARCHAR(255) NOT NULL, + pf_server VARCHAR(255) +); +CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server); + +CREATE TABLE protected_titles ( + pt_namespace SMALLINT NOT NULL, + pt_title VARCHAR(255) NOT NULL, + pt_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL, + pt_reason clob(1K), + pt_timestamp TIMESTAMP NOT NULL, + pt_expiry TIMESTAMP , + pt_create_perm VARCHAR(255) NOT NULL DEFAULT '' +); +CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title); + + + +CREATE TABLE updatelog ( + ul_key VARCHAR(255) NOT NULL PRIMARY KEY +); + +CREATE SEQUENCE category_id_seq; +CREATE TABLE category ( + cat_id INTEGER NOT NULL PRIMARY KEY, + --PRIMARY KEY DEFAULT nextval('category_id_seq'), + cat_title VARCHAR(255) NOT NULL, + cat_pages INTEGER NOT NULL DEFAULT 0, + cat_subcats INTEGER NOT NULL DEFAULT 0, + cat_files INTEGER NOT NULL DEFAULT 0, + cat_hidden SMALLINT NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX category_title ON category(cat_title); +CREATE INDEX category_pages ON category(cat_pages); + +CREATE TABLE mediawiki_version ( + type VARCHAR(255) NOT NULL, + mw_version VARCHAR(255) NOT NULL, + notes VARCHAR(255) , + + pg_version VARCHAR(255) , + pg_dbname VARCHAR(255) , + pg_user VARCHAR(255) , + pg_port VARCHAR(255) , + mw_schema VARCHAR(255) , + ts2_schema VARCHAR(255) , + ctype VARCHAR(255) , + + sql_version VARCHAR(255) , + sql_date VARCHAR(255) , + cdate TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP +); + +INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date) + VALUES ('Creation','??','$LastChangedRevision: 34049 $','$LastChangedDate: 2008-04-30 10:20:36 -0400 (Wed, 30 Apr 2008) $'); +